JavaScript 学习笔记之闭包


  1. 访问另一函数的变量
  2. 保存变量
  3. that 与 this

好久没更新 JavaScript 基础知识这一块,这次接着总结一下我对闭包的理解。

变量的作用域

在理解闭包之前,我们先理解一下 JavaScript 的变量作用域,ES5 的变量作用域主要就是两种,全局作用域与局部作用域。
全局作用域是函数内部都可以访问到全局变量,而局部作用域则是函数外部无法访问到的函数内部的局部变量。这里要注意一点是,声明变量一定要带上声明的关键字 var let const 等,否则会默认声明一个全局的变量。

闭包

那么闭包又是怎样?
由于作用域的这个特性,外部函数想要直接访问内部函数的变量是不可以的,可是很多时候我们需要访问到函数内的局部变量那应该怎么办?那我们可以变通一下:
在函数内部再定义一个函数:

1
2
3
4
5
6
7
8
9
10
function func1() {
var n = 999;
function func2() {
n += 1;
console.log(n);
}
return func2;
}
var result = func1();
result();//1000

这个函数就是高程中提到的闭包:闭包是指有权访问另一个函数作用域中的变量的函数

闭包的用途

那闭包有什么用?
主要分为两个部分:
1.访问函数内部的变量
2.在内存中保存变量

上面闭包的例子我们可以看到闭包本身的特性解决访问函数内部变量的问题,那么在内存中保存变量怎么理解?我们继续使用上面的例子:

1
2
3
4
5
6
7
8
9
10
11
function func1() {
var n = 999;
function func2() {
n += 1;
console.log(n);
}
return func2;
}
var result = func1();
result();//1000
result();//1001

我们可以看到每执行一次 result(),n 逐一增加。
一般来说,调用函数时,会创建一个对象来保存它的局部变量,并且把这个变量添加到作用域链上面,在函数执行完后局部变量就会被垃圾回收机制回收,就会从作用域链中删除这个局部变量的对象。但是像闭包这类,定义了一个内部的嵌套函数,那这个嵌套函数就会有自己相应的作用域链,它和外部函数一样,会局部变量保存到一个对象中去,如果嵌套函数是作为返回值返回或是周围对象的属性时,就后有一个外部指引指向该嵌套函数,那么外部函数就不会被当成垃圾回收,而它在作用域中绑定的局部变量也不会被回收。

那一开始我提到的 that 与 this 又是什么?
在应用闭包时,很容易对引用的 this 产生误解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var name = 'window';
var person1 = {
name: 'local',
getName: function() {
return function() {
return this.name;
}
}
}
var person2 = {
name: 'local',
getName: function() {
return this.name;
}
}
alert(person1.getName()());//window
alert(person2.getName());//local

注意一点,匿名函数的创建会创建一个 this 指向全局的 window。
为避免引起不必要的误会,我们可以这样做:

1
2
3
4
5
6
7
8
9
10
var name = 'window';
var person = {
name: 'local',
gatName: function() {
var that = this;
return function() {
return that.name;
}
}
}